LambdaからAurora(PostgreSQL)へIAM認証を使用した接続で失敗する原因と対処について
テクニカルサポートノート サービス名:Lambda, Aurora PostgreSQL
困っていた内容
LambdaからAurora(PostgreSQL)へIAM認証を使用した接続をするために、下記AWSブログを参考にしたが、エラーが発生してうまくいかない。
▼発生するエラー
{ "status": "Error", "message": "connect() got an unexpected keyword argument 'ssl'" }
なお、今回の環境は下記バージョンで確認を行っております。
・Lambdaのランタイム: Python 3.7
・Aurora(PostgreSQL): 10.14
どうすればいいか?
基本的にブログの内容通りに進めていただいて構いません。
ただし、最後のサンプルコードには誤りと廃止されたパラメータが使われている箇所があり、そのままコピペ実行してもうまくいきません。具体的には下記5点によるものです。
- osモジュールのインポートが不足している。
- 24行目のpg8000.connectメソッドのssl_contextパラメータはpg8000(1.14.0)から廃止されている。
- 33行目に不要なdecodeメソッドが使われている。
- 37行目のresult変数名は誤り。("s"が足りない。)
- 39行目と41行目のダブルクォーテーションに誤った文字が使われている。
2点目の内容について補足します。
GitHubにあるpg8000のチェンジログを確認しますと、sslパラメータが廃止された点について記載されています。また、APIドキュメントには代わりとなるssl_contextパラメータが記載されていますので、こちらを使用して修正版のコードを書いてみたいと思います。※:ブログが公開された後に変更が行われた様です。
The ssl.wrap_socket function is deprecated, so we now give the user the option of using a default SSLContext or to pass in a custom one. This is a backwardly incompatible change.
ssl_contextThis governs SSL encryption for TCP/IP sockets. It can have three values:・None, meaning no SSL (the default)・True, means use SSL with an ssl.SSContext created using ssl.create_default_context()・An instance of ssl.SSContext which will be used to create the SSL connection.
ということで、修正版のコードが下記です。
import os import boto3 import pg8000 def lambda_handler(event, context): try: # rds のサービス名で低レベルクライアントを作成する client = boto3.client("rds") # 環境変数を読み取り、DB EndPoint を取得する DBEndPoint = os.environ.get("DBEndPoint") # 環境変数を読み取り、データベース名を取得する DatabaseName = os.environ.get("DatabaseName") # 環境変数を読み取り、データベースにアクセスできるデータベースユーザー名を取得します。 DBUserName = os.environ.get("DBUserName") # IAM 認証情報を使用してデータベースに接続するために使用する認証トークンを生成します。 password = client.generate_db_auth_token( DBHostname=DBEndPoint, Port=5432, DBUsername=DBUserName ) # パスワードとして生成されたトークンを使用してサーバーとの接続を確立する conn = pg8000.connect( host=DBEndPoint, user=DBUserName, database=DatabaseName, password=password, ssl_context=True, ) # カーソルオブジェクトのインスタンス化 cursor = conn.cursor() # 実行されるクエリ query = "SELECT CURRENT_DATABASE()" # 接続されたデータベースでクエリ/コマンドを実行する cursor.execute(query) # 列名を取得する columns = [str(desc[0]) for desc in cursor.description] results = [] # 結果セットから dict の行を作成します。 for res in cursor: results.append(dict(zip(columns, res))) # 結果を返す return {"status": "Success", "results": results} except Exception as e: return {"status": "Error", "message": str(e)}
うまくいくと、Lambdaの実行結果として下記内容が記録されるはずです。
{ "status": "Success", "results": [ { "current_database": "<接続したDatabase名>" } ] }